package org.beetl.sql.core.mapping.join;

import org.beetl.sql.clazz.kit.BeanKit;
import org.beetl.sql.clazz.kit.BeetlSQLException;
import org.beetl.sql.clazz.kit.CaseInsensitiveHashMap;
import org.beetl.sql.core.ExecuteContext;
import org.beetl.sql.core.mapping.BeanProcessor;
import org.beetl.sql.core.mapping.ResultSetMapper;
import org.beetl.sql.core.mapping.type.ReadTypeParameter;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 通过配置来实现映射，配置可以是任意格式，比如json或者xml，或者其他格式，子类实现parse方法将这些特定格式配置转化为{@link AttrNode}
 *
 * <pre>
 * {@code
 * @ResultProvider(JsonConfigJoinMapper.class)
 * @JsonMapper("{'id':'id','name':'name','users':{'id':'u_id','name':'u_name'}}")
 * public class MyDepartment {
 *     Integer id;
 *     String name;
 *     List<MyUser>  users;
 * }
 * }
 * </pre>
 *
 * <pre>
 * {@code
 * SQLReady ready = new SQLReady("select d.id id,d.name name ,u.id u_id,u.name u_name
 *      from department d join user u on d.id=u.department_id ");
 * List<MyDepartment> list = sqlManager.execute(ready,MyDepartment.class);
 * }
 * </pre>
 * {@code AttrNode},AttrNode实现了visit模式，通过遍历ResultSet获得结果集
 *
 * 需要注意的，目前任何ConfigMapper实现映射，都需要确保树节点每层都有值，因为JsonConfigMapper会依据这些值作为key
 * 确保不会重建对象。
 * <br/>
 * 如下是合法的，因为第一层是id和name：
 * <br/>
 * {@code select d.id id,d.name name ,u.id `u.id`,u.name `u.name`}
 * <br/>
 * 这个是不合法的,因为第一层无值：
 * <br/>
 * {@code select d.id `d.id` ,d.name 'd.name' ,u.id u_id,u.name u_name}
 * <br/>
 * 参考 {@link AttrNode#getNodeValueFromResultSet}
 *
 * @author xiandafu
 * @see  org.beetl.sql.annotation.entity.ResultProvider
 */
public abstract class ConfigJoinMapper implements ResultSetMapper {


	@Override
	public List mapping(ExecuteContext ctx, Class target, ResultSet resultSet, Annotation config) {

		try {
			/*第一步，将本次映射规则转换为一颗AttrNode树*/
			ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
			AttrNode root = parse(ctx, target, resultSetMetaData, config);
			/*用于结果集数据类型转换的对象*/
			ReadTypeParameter rtp = new ReadTypeParameter(ctx.sqlId, ctx.sqlManager.getDbStyle().getName(), target,
					resultSet, resultSetMetaData, 1, ctx);
			RenderContext renderContext = new RenderContext();
			renderContext.beanProcessor = ctx.sqlManager.getDefaultBeanProcessors();
			while (resultSet.next()) {
				/*每行结果集数据进行遍历处理*/
				root.visit(renderContext, ctx, rtp);
			}
			if (renderContext.grid.gridValues.isEmpty()) {
				return new ArrayList();
			}
			List<NodeValue> nodeValues = renderContext.grid.getAll(root);

			return nodeValues.stream().map(nodeValue -> nodeValue.objectWrapper.realObject)
					.collect(Collectors.toList());

		} catch (SQLException ex) {
			throw new BeetlSQLException(BeetlSQLException.SQL_EXCEPTION, ex);
		}catch (BeetlSQLException ex){
			throw  ex;
		}
		catch (Exception ex) {
			throw new BeetlSQLException(BeetlSQLException.SQL_EXCEPTION, ex);
		}
	}


	protected Map<String, Integer> getColumnIndex(ResultSetMetaData rsmd) throws SQLException {
		int count = rsmd.getColumnCount();
		Map<String, Integer> map = new CaseInsensitiveHashMap();
		for (int col = 1; col <= count; col++) {
			String columnName = rsmd.getColumnLabel(col);
			if (null == columnName || 0 == columnName.length()) {
				columnName = rsmd.getColumnName(col);
			}
			map.put(columnName, col);
		}
		return map;
	}


	/**
	 * 子类继承,根据映射配置获得一个树结构,需要考虑缓存以避免每次解析
	 * @param target
	 * @param rsmd
	 * @return
	 * @throws SQLException
	 */
	abstract protected AttrNode parse(ExecuteContext ctx, Class target, ResultSetMetaData rsmd, Annotation config)
			throws Exception;


	static class RenderContext {
		Grid grid = new Grid();
		ObjectWrapper parent = null;
		BeanProcessor beanProcessor = null;
	}

	/**
	 * 执行节点得到一堆值
	 */
	protected static class NodeValue {
		/**
		 * 是否是一个有效节点，无效节点不进行映射
		 */
		boolean isValid = true;
		Object key;
		/**
		 * 已经从数据表中获取好了当前节点的数据。属性->值
		 */
		Map<String, Object> value;
		//value对应的真实对象
		ObjectWrapper objectWrapper;

		NodeValue() {
		}

		NodeValue(boolean isValid) {
			this.isValid = isValid;
		}

		/**
		 * 如果无key，则是使用属性本生
		 * @param values
		 */
		public NodeValue(Map values) {
			//如果没有key，则使用一个临时key
			this.key = new TempKey(values);
			this.value = values;
		}

		public NodeValue(Object key, Map values) {
			this.key = key;
			this.value = values;
		}

		static class TempKey {
			Map<String, Object> value;
			int hashCode;

			public TempKey(Map<String, Object> value) {
				this.value = value;
				hashCode = value.hashCode();
			}

			@Override
			public boolean equals(Object o) {
				if (this == o) {
					return true;
				}
				if (o == null || getClass() != o.getClass()) {
					return false;
				}
				TempKey tempKey = (TempKey) o;
				return value.equals(tempKey.value);
			}

			@Override
			public int hashCode() {
				return hashCode;
			}
		}
	}

	/**
	 * 遍历节点后得到的NodeValue，然后转化为ObjectWrapper，里面包含了实际生成的对象
	 */
	protected static class ObjectWrapper {
		/**
		 * 真实对象的类型
		 */
		Class target;
		/**
		 * 实际对象
		 */
		Object realObject;
		/**
		 * 关联的节点值对象
		 */
		NodeValue fromNodeValue;
		/**
		 * 标记此属性已经具备的NodeValue，避免重复加入到此对象
		 */
		Map<PropertyDescriptor, Set> flagMap = new HashMap<>();

		/**
		 * 最终将节点中的列数据赋值给真正的对象
		 *
		 * @param propertyMap 属性名->PropertyDescriptor
		 * @throws Exception 异常
		 */
		public void makeObject(Map<String, PropertyDescriptor> propertyMap) throws Exception {
			if (Map.class.isAssignableFrom(target)) {
				realObject = fromNodeValue.value;
			} else {
				//copy成对象
				Object obj = BeanKit.newInstance(target);
				Map<String, Object> valueMap = fromNodeValue.value;
				for (Map.Entry<String, Object> entry : valueMap.entrySet()) {
					String attr = entry.getKey();
					Object value = entry.getValue();
					PropertyDescriptor ps = propertyMap.get(attr);
					try {
						ps.getWriteMethod().invoke(obj, value);
					} catch (IllegalArgumentException illegalArgumentException) {
						throw new IllegalArgumentException(ps + " 属性不匹配 " + ps.getName() + " for " + value.getClass());
					}
				}
				this.realObject = obj;
			}
			fromNodeValue.objectWrapper = this;
		}
	}

	/**
	 * 保存遍历ResultSet后的了Node执行生成的值，可以理解为缓存。
	 */
	protected static class Grid {

		Map<AttrNode, Set<Object>> gridKeys = new HashMap<>();
		/**
		 *
		 */
		Map<AttrNode, List<NodeValue>> gridValues = new HashMap<>();

		public void push(AttrNode node, NodeValue nodeValue) {
			Set<Object> cell = gridKeys.get(node);
			if (cell == null) {
				cell = new HashSet<>();
				gridKeys.put(node, cell);
				gridValues.put(node, new ArrayList<>());
			}
			if (!cell.contains(nodeValue.key)) {
				cell.add(nodeValue.key);
				List<NodeValue> nodeValueArrayList = gridValues.get(node);
				nodeValueArrayList.add(nodeValue);
			}

		}


		public boolean contain(AttrNode node, Object key) {
			Set<Object> set = gridKeys.get(node);

			if (set == null) {
				return false;
			}
			return set.contains(key);

		}

		public NodeValue getNodeValue(AttrNode node, Object key) {
			List<NodeValue> cell = gridValues.get(node);
			if (cell == null) {
				return null;
			}
			//TODO 效率提升
			for (NodeValue nodeValue : cell) {
				if (nodeValue.key.hashCode() == key.hashCode() && nodeValue.key.equals(key)) {
					return nodeValue;
				}
			}
			return null;

		}

		public List<NodeValue> getAll(AttrNode node) {
			List<NodeValue> cell = gridValues.get(node);
			return cell;
		}
	}


}
